package org.dreamwork.websocket;

import org.apache.log4j.Logger;
import org.dreamwork.util.StringUtil;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by game on 2017/2/16
 */
public class WebSocketManager {
    private static final Logger logger = Logger.getLogger (WebSocketManager.class);
    private static final Object LOCKER = new byte[0];
    private static final Map<Class<? extends IWebSocketExecutor>, Set<IWebSocketExecutor<?>>> map =
            new HashMap<Class<? extends IWebSocketExecutor>, Set<IWebSocketExecutor<?>>> ();
    private static final ExecutorService service = Executors.newCachedThreadPool ();

    public static Collection<IWebSocketExecutor<?>> getActiveWebSocketExecutor () {
        synchronized (map) {
            Set<IWebSocketExecutor<?>> set = new HashSet<IWebSocketExecutor<?>> ();
            for (Set<IWebSocketExecutor<?>> s : map.values ()) {
                set.addAll (s);
            }
            return set;
        }
    }

    public static void add (IWebSocketExecutor<?> socket) {
        synchronized (LOCKER) {
            if (socket == null) {
                throw new NullPointerException ();
            }

            Class<? extends IWebSocketExecutor> type = socket.getClass ();
            Set<IWebSocketExecutor<?>> set = map.get (type);
            if (set == null) {
                set = new HashSet<IWebSocketExecutor<?>> ();
                map.put (type, set);
            }
            set.add (socket);
        }

        if (logger.isDebugEnabled ()) {
            logger.debug ("a client added: " + socket);
        }
    }

    public static void remove (IWebSocketExecutor<?> socket) {
        synchronized (LOCKER) {
            if (socket == null) {
                return;
            }

            Class<? extends IWebSocketExecutor> type = socket.getClass ();
            Set<IWebSocketExecutor<?>> set = map.get (type);
            if (set != null) {
                set.remove (socket);
            }
        }

        if (logger.isDebugEnabled ()) {
            logger.debug ("a client removed: " + socket);
        }
    }

    @SuppressWarnings ("unchecked")
    private static <T> Set<IWebSocketExecutor<T>> copy (Class<? extends IWebSocketExecutor> type) {
        synchronized (LOCKER) {
            Set set = map.get (type);
            if (set == null) {
                return null;
            }

            return new HashSet<> (set);
        }
    }

    public static<T> void fireDataChanged (Class<? extends IWebSocketExecutor> type, T message) {
        if (type == null || message == null) {
            throw new NullPointerException ();
        }

        Set<IWebSocketExecutor<T>> copy = copy (type);
        if (copy != null) {
            for (IWebSocketExecutor<T> client : copy) {
                try {
                    if (client.matches (message)) {
                        client.send (message);
                    }
                } catch (Exception ex) {
                    ex.printStackTrace ();
                }
            }
        }
    }

    public static<T> void fireDataChanged (Class<? extends IWebSocketExecutor> type, String id, T message) {
        if (StringUtil.isEmpty (id)) {
            fireDataChanged (type, message);
        }

        Set<IWebSocketExecutor<T>> copy = copy (type);
        if (copy != null) {
            for (IWebSocketExecutor<T> client : copy) {
                try {
                    if (client.matches (id, message)) {
                        client.send (message);
                    }
                } catch (Exception ex) {
                    ex.printStackTrace ();
                }
            }
        }
    }

    protected static void run (IWebSocketExecutor<?> executor) {
        service.execute (executor);
    }

    public static void shutdown () throws IOException {
        List<Runnable> list = service.shutdownNow ();
        if (!list.isEmpty ()) {
            for (Runnable executor : list) {
                ((IWebSocketExecutor<?>) executor).close ();
            }
        }
    }
}